#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "tests/common_test.h"

/*
pipe为匿名管道。
使用匿名管道，通信的进程之间需要一个父子关系，通信的两个进程一定是由一个共同的祖先进程启动，匿名管道没数据交叉的问题；

从概念上讲，管道是两个进程之间的连接，一个进程的标准输出成为另一个进程的标准输入。在UNIX操作系统中，管道用于进程间通信。
管道只是单向通信，即我们可以这样使用管道：一个进程向管道写入数据，另一个进程从管道读取数据。管道，是内存中被视为“虚拟文件”的一块区域。
管道可以被创建进程及其所有子进程读写。一个进程可以写入这个“虚拟文件”或管道，另一个相关进程可以从中读取。
如果在某个内容写入管道之前，某个进程试图读取该内容，则该进程将挂起，直到内容被写入。
管道系统调用在进程的打开文件表中找到前两个可用位置，并将其分配给管道的读取和写入端。
管道表现为FIFO（先进先出），管道实现类似队列数据结构。读写大小不是必须一致。在管道中，我们可以一次写入512字节，但可以一次只读取1字节。

int pipe(int fds[2]);

参数：
    fd[0] 将是管道读取端的fd（文件描述符）
    fd[1] 将是管道写入端的fd
返回值：0表示成功，-1表示失败。

https://blog.csdn.net/zsx0728/article/details/113830964
*/

#define MSGSIZE 16
char *msg1 = "hello, world #1";
char *msg2 = "hello, world #2";
char *msg3 = "hello, world #3";

static void test_pipe_sync(void)
{
    TEST_START_INFO;
    char inbuf[MSGSIZE];
    int p[2];

    if (pipe(p) < 0)
        return;

    /* continued */
    /* write pipe */
    write(p[1], msg1, MSGSIZE);
    write(p[1], msg2, MSGSIZE);
    write(p[1], msg3, MSGSIZE);

    /* read pipe */
    read(p[0], inbuf, MSGSIZE);

    printf("%s|\n", msg1);
    printf("%s|\n", inbuf);

    printf("%lu\n", strlen(msg1));
    printf("%lu\n", strlen(inbuf));

    /* 下面两句是等价的 */
    assert(!strncmp(msg1, inbuf, MSGSIZE));
    assert(!strcmp(msg1, inbuf));

    read(p[0], inbuf, MSGSIZE);
    assert(!strcmp(msg2, inbuf));

    /* 可以以指定的长度读取管道中的数据 */
    read(p[0], inbuf, MSGSIZE - 3);
    assert(!strncmp("hello, world ", inbuf, MSGSIZE - 3));

    read(p[0], inbuf, 2);
    assert(!strncmp("#3", inbuf, 2));

    assert('l' == inbuf[2]);
    assert('o' == inbuf[4]);

    close(p[0]);
    close(p[1]);
}

static void test_pipe_async(void)
{
    char inbuf[MSGSIZE];
    int p[2], pid, nbytes, r;

    r = pipe(p);
    assert(-1 != r);

    TEST_START_INFO;

    /*

    continued
    子进程fork()返回0，父进程返回子进程id>0

    */
    if ((pid = fork()) > 0)
    {
        write(p[1], msg1, MSGSIZE);
        write(p[1], msg2, MSGSIZE);
        write(p[1], msg3, MSGSIZE);

        // Adding this line will
        // not hang the program
        // close(p[1]);
        wait(NULL);
    }
    else
    {
        // Adding this line will
        // not hang the program
        // close(p[1]);
        while ((nbytes = read(p[0], inbuf, MSGSIZE)) > 0)
            printf("%s\n", inbuf);
        if (nbytes != 0)
            exit(2);
        printf("Finished reading\n");
    }
}

static void test_fork(void)
{
    /*
    程序在执行到fork的时候克隆出了子进程，子进程具有当前进程的一切资源。
    子进程从fork开始执行，并不执行fork前面的代码。

    fork的返回值有三种
        0：子进程返回0
        大于0：父进程返回子进程pid
        -1：创建失败
    */

    pid_t pid, ppid;
    int i = 0;

    ppid = getpid();
    printf("x,current thread pid is %d \n", ppid);
    assert(ppid > 0);

    pid = fork();
    assert(-1 != pid);

    if (0 == pid)
    {
        // 这是子进程
        assert(ppid == getppid());
    }

    if (0 < pid)
    {
        // 这是父进程
    }

    i++;
    printf("i = %d \n", i);
    printf("x,this pid is %d , current pid is %d \n", pid, getpid());
}

int main(int argc, char *argv[])
{
    (void)argc;
    (void)argv;

    test_pipe_sync();
    // test_pipe_async();
    test_fork();
    return 0;
}
